#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/conf.h>
#include <uvm/uvm_extern.h>
#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/pmap.h>
#include <machine/cpu.h>
#include <machine/conf.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/rasops/rasops.h>
#include <machine/fbvar.h>
#include <dev/sbus/sbusvar.h>
#define PX_PROM0_OFFSET 0x000000
#define PX_OVERLAY_OFFSET 0x010000
#define PX_REG_OFFSET 0x040000
#define PX_CCUBE_OFFSET 0x050000
#define PX_PLANE8_OFFSET 0x080000
#define PX_PLANE24_OFFSET 0x200000
#define PX_PROM1_OFFSET 0x7f0000
#define PX_MAP_SIZE 0x800000
#define PX_REG_DISPKLUGE 0x00b8
#define DISPKLUGE_DEFAULT 0xc41f
#define DISPKLUGE_BLANK (1 << 12)
#define DISPKLUGE_SYNC (1 << 13)
#define PX_REG_BT463_RED 0x0480
#define PX_REG_BT463_GREEN 0x0490
#define PX_REG_BT463_BLUE 0x04a0
#define PX_REG_BT463_ALL 0x04b0
#define PX_REG_SIZE 0x04d0
struct tvtwo_softc {
struct sunfb sc_sunfb;
bus_space_tag_t sc_bustag;
bus_addr_t sc_paddr;
volatile u_int8_t *sc_m8;
volatile u_int8_t *sc_m24;
volatile u_int8_t *sc_regs;
int sc_nscreens;
};
int tvtwo_ioctl(void *, u_long, caddr_t, int, struct proc *);
paddr_t tvtwo_mmap(void *, off_t, int);
void tvtwo_burner(void *, u_int, u_int);
struct wsdisplay_accessops tvtwo_accessops = {
.ioctl = tvtwo_ioctl,
.mmap = tvtwo_mmap,
.burn_screen = tvtwo_burner
};
void tvtwo_directcmap(struct tvtwo_softc *);
static __inline__
void tvtwo_ramdac_wraddr(struct tvtwo_softc *, u_int32_t);
void tvtwo_reset(struct tvtwo_softc *, u_int);
void tvtwo_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);
int tvtwomatch(struct device *, void *, void *);
void tvtwoattach(struct device *, struct device *, void *);
const struct cfattach tvtwo_ca = {
sizeof(struct tvtwo_softc), tvtwomatch, tvtwoattach
};
struct cfdriver tvtwo_cd = {
NULL, "tvtwo", DV_DULL
};
#define NFREQCODE 5
const int defwidth[NFREQCODE] = { 1152, 1152, 1152, 1024, 640 };
const int defheight[NFREQCODE] = { 900, 900, 900, 768, 480 };
int
tvtwomatch(struct device *parent, void *vcf, void *aux)
{
struct sbus_attach_args *sa = aux;
if (strcmp(sa->sa_name, "PGI,tvtwo") == 0 ||
strcmp(sa->sa_name, "PGI,tvthree") == 0)
return (1);
return (0);
}
void
tvtwoattach(struct device *parent, struct device *self, void *args)
{
struct tvtwo_softc *sc = (struct tvtwo_softc *)self;
struct sbus_attach_args *sa = args;
bus_space_tag_t bt;
bus_space_handle_t bh;
int node, width, height, freqcode;
int isconsole;
char *freqstring, *revision;
bt = sa->sa_bustag;
node = sa->sa_node;
printf(": %s", getpropstring(node, "model"));
revision = getpropstring(node, "revision");
if (*revision != '\0')
printf(", revision %s", revision);
if (sa->sa_nreg > 1) {
sa->sa_offset -= PX_REG_OFFSET;
sa->sa_size = PX_MAP_SIZE;
}
isconsole = node == fbnode;
sc->sc_bustag = bt;
if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + PX_REG_OFFSET,
PX_REG_SIZE, BUS_SPACE_MAP_LINEAR, 0, &bh) != 0) {
printf("%s: couldn't map registers\n", self->dv_xname);
return;
}
sc->sc_regs = bus_space_vaddr(bt, bh);
freqstring = getpropstring(node, "freqcode");
if (*freqstring != '\0') {
freqcode = (int)*freqstring;
if (freqcode == 'g') {
width = height = 1024;
} else {
if (freqcode < '1' || freqcode > '6')
freqcode = 0;
else
freqcode -= '1';
width = defwidth[freqcode];
height = defheight[freqcode];
width = getpropint(node, "hres", width);
height = getpropint(node, "vres", height);
}
} else {
width = 1152;
height = 900;
}
sc->sc_sunfb.sf_depth = 8;
sc->sc_sunfb.sf_width = width;
sc->sc_sunfb.sf_height = height;
sc->sc_sunfb.sf_linebytes = width >= 1024 ? width : 1024;
sc->sc_sunfb.sf_fbsize = sc->sc_sunfb.sf_linebytes * height;
printf(", %dx%d\n", sc->sc_sunfb.sf_width, sc->sc_sunfb.sf_height);
sc->sc_paddr = sbus_bus_addr(bt, sa->sa_slot, sa->sa_offset);
if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + PX_PLANE8_OFFSET,
round_page(sc->sc_sunfb.sf_fbsize), BUS_SPACE_MAP_LINEAR, 0,
&bh) != 0) {
printf("%s: couldn't map 8-bit video plane\n", self->dv_xname);
return;
}
sc->sc_m8 = bus_space_vaddr(bt, bh);
if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + PX_PLANE24_OFFSET,
round_page(4 * sc->sc_sunfb.sf_fbsize), BUS_SPACE_MAP_LINEAR, 0,
&bh) != 0) {
printf("%s: couldn't map 32-bit video plane\n", self->dv_xname);
return;
}
sc->sc_m24 = bus_space_vaddr(bt, bh);
tvtwo_burner(sc, 1, 0);
sc->sc_sunfb.sf_ro.ri_hw = sc;
sc->sc_sunfb.sf_ro.ri_bits = (u_char *)sc->sc_m8;
fbwscons_init(&sc->sc_sunfb, 0, isconsole);
fbwscons_setcolormap(&sc->sc_sunfb, tvtwo_setcolor);
if (isconsole)
fbwscons_console_init(&sc->sc_sunfb, -1);
fbwscons_attach(&sc->sc_sunfb, &tvtwo_accessops, isconsole);
}
int
tvtwo_ioctl(void *dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
struct tvtwo_softc *sc = dev;
struct wsdisplay_fbinfo *wdf;
switch (cmd) {
case WSDISPLAYIO_GTYPE:
*(u_int *)data = WSDISPLAY_TYPE_XVIDEO;
break;
case WSDISPLAYIO_GINFO:
wdf = (struct wsdisplay_fbinfo *)data;
wdf->height = sc->sc_sunfb.sf_height;
wdf->width = sc->sc_sunfb.sf_width;
wdf->depth = 32;
wdf->stride = sc->sc_sunfb.sf_linebytes * 4;
wdf->offset = 0;
wdf->cmsize = 0;
break;
case WSDISPLAYIO_GETSUPPORTEDDEPTH:
*(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
break;
case WSDISPLAYIO_LINEBYTES:
*(u_int *)data = sc->sc_sunfb.sf_linebytes * 4;
break;
case WSDISPLAYIO_GETCMAP:
case WSDISPLAYIO_PUTCMAP:
break;
case WSDISPLAYIO_SMODE:
if (*(int *)data == WSDISPLAYIO_MODE_EMUL) {
tvtwo_reset(sc, 8);
} else {
tvtwo_reset(sc, 32);
}
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);
}
paddr_t
tvtwo_mmap(void *v, off_t offset, int prot)
{
struct tvtwo_softc *sc = v;
if (offset & PGOFSET)
return (-1);
if (offset >= 0 && offset < sc->sc_sunfb.sf_fbsize * 4) {
return (bus_space_mmap(sc->sc_bustag, sc->sc_paddr,
PX_PLANE24_OFFSET + offset, prot, BUS_SPACE_MAP_LINEAR));
}
return (-1);
}
void
tvtwo_burner(void *v, u_int on, u_int flags)
{
struct tvtwo_softc *sc = v;
u_int32_t dispkluge;
if (on)
dispkluge = DISPKLUGE_DEFAULT & ~DISPKLUGE_BLANK;
else {
dispkluge = DISPKLUGE_DEFAULT | DISPKLUGE_BLANK;
if (flags & WSDISPLAY_BURN_VBLANK)
dispkluge |= DISPKLUGE_SYNC;
}
*(volatile u_int32_t *)(sc->sc_regs + PX_REG_DISPKLUGE) = dispkluge;
}
void
tvtwo_reset(struct tvtwo_softc *sc, u_int depth)
{
if (depth == 32) {
tvtwo_directcmap(sc);
} else {
fbwscons_setcolormap(&sc->sc_sunfb, tvtwo_setcolor);
}
}
static __inline__ void
tvtwo_ramdac_wraddr(struct tvtwo_softc *sc, u_int32_t addr)
{
volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED);
dac[0] = (addr & 0xff);
dac[1] = ((addr >> 8) & 0xff);
}
void
tvtwo_directcmap(struct tvtwo_softc *sc)
{
volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED);
u_int32_t c;
tvtwo_ramdac_wraddr(sc, 0);
for (c = 0; c < 256; c++) {
dac[3] = c;
dac[3] = c;
dac[3] = c;
}
}
void
tvtwo_setcolor(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t b)
{
struct tvtwo_softc *sc = v;
volatile u_int32_t *dac = (u_int32_t *)(sc->sc_regs + PX_REG_BT463_RED);
tvtwo_ramdac_wraddr(sc, index);
dac[3] = r;
dac[3] = g;
dac[3] = b;
}